package vb.week1.symtab;


public class Parse
{

  private enum State
  {
    Init, Decl, Use, Id, Letter
  }

  private SymbolTable<IdEntry> symbolTable;

  public static void main(String[] args)
  {

    Parse parse = new Parse();
    String code = "(D:xD:yD:zU:yU:z(D:aD:zU:xU:yU:z(D:pD:qU:pU:y)(U:zD:zU:z)U:a)(D:xD:pD:xU:xU:qU:yU:z))";
    String code2 = "(D:aD:y D:z" + "    U:y U:z" + "    (D:a D:z"
        + "        U:x U:y U:z (D:p D:q U:p U:y) (U:z D:z U:z) U:a" + "    )"
        + "    (D:x D:p D:x" + "        U:x U:q U:y U:z" + "    )" + ")";

    boolean x = parse.parse(code2);

    // System.out.println(x);
  }

  public boolean parse(String s)
  {
    boolean result = true;

    s = s.replaceAll("\\s", "");
    // System.out.println(s);
    // System.out.println(s.length());

    if (s.isEmpty() || s.charAt(0) != '(')
      return false;

    int i = 0;
    char c;
    State x = State.Init;
    String id = "";
    State type = State.Use;
    do
    {
      c = s.charAt(i++);

      switch (c)
      {
      case '(':
        if (!id.isEmpty())
        {
          insertOrCheck(id, type);
          id = "";
        }
        if (x == State.Letter || x == State.Init)
          symbolTable.openScope();
        else
          result = false;
        break;
      case ')':
        if (!id.isEmpty())
        {
          insertOrCheck(id, type);
          id = "";
        }
        if (x == State.Letter)
          symbolTable.closeScope();
        else
          result = false;
        break;
      case 'U':
        if (!id.isEmpty())
        {
          insertOrCheck(id, type);
          id = "";
        }
        if (x == State.Letter || x == State.Init)
        {
          type = x = State.Use;
        }
        else
          result = false;
        break;
      case 'D':
        if (!id.isEmpty())
        {
          insertOrCheck(id, type);
          id = "";
        }
        if (x == State.Letter || x == State.Init)
          type = x = State.Decl;
        else
          result = false;
        break;
      case ':':
        if (x == State.Decl || x == State.Use)
          x = State.Id;
        else
          result = false;
        break;
      case 'a':
      case 'b':
      case 'c':
      case 'd':
      case 'e':
      case 'f':
      case 'g':
      case 'h':
      case 'i':
      case 'j':
      case 'k':
      case 'l':
      case 'm':
      case 'n':
      case 'o':
      case 'p':
      case 'q':
      case 'r':
      case 's':
      case 't':
      case 'u':
      case 'v':
      case 'w':
      case 'x':
      case 'y':
      case 'z':
        if (x == State.Id || x == State.Letter)
        {
          x = State.Letter;
          id = id + c;
        }
        else
          result = false;
        break;
      default:
        result = false;
      }
    } while (result && i < s.length() && symbolTable.currentLevel() >= 0);

    // System.out.println(i);

    if (symbolTable.currentLevel() != -1 || i < s.length())
      result = false;

    return result;
  }

  private void insertOrCheck(String id, State type)
  {
    if (type == State.Decl)
    {
      try
      {
        symbolTable.enter(id, new IdEntry());
        System.out.println(id + " declared at " + symbolTable.currentLevel() + ".");
      }
      catch (SymbolTableException e)
      {
        System.out.println(e.getMessage());
      }
    }
    else
    {
      IdEntry i = symbolTable.retrieve(id);
      if (i != null)
        System.out.println(id + " declared at " + i.getLevel() + ", used at " + symbolTable.currentLevel() + ".");
      else
        System.out.println(id + " undeclared, used at " + symbolTable.currentLevel() + ".");
    }
    // System.out.println(id + ", " + type); 
  }
  
  public Parse()
  {
    this.symbolTable = new SymbolTable<IdEntry>();
  }
}
